package lab.cadl.lirui.markdown.core.utils

import org.apache.commons.io.IOUtils
import java.io.File
import java.net.URL
import java.nio.file.Files
import java.nio.file.Paths


class ImageEntry(val file: File, val id1: Int, val id2: Int)

interface ImageCache {
    val baseDir: String
    fun registerImage(url: String, postfix: String = ""): File
    fun fetchImage(url: String): ImageEntry
    fun createDelegation(baseDir: String): ImageCache
}

class ImageCacheImpl(override var baseDir: String, tmpDir_: String = "/tmp", private val deleteOnExit: Boolean = false) : AutoCloseable, ImageCache {
    companion object {
        val LOG by logger()

        const val DEFAULT_EXT: String = ".png"
    }

    private val tmpDir: File = Paths.get(tmpDir_, "__cache__").toFile()
    private val cache: MutableMap<String, ImageEntry> = mutableMapOf()
    private val id1 = 1
    private var nextId2 = 1

    init {
        if (!tmpDir.exists()) {
            Files.createDirectories(tmpDir.toPath())
        }
    }

    private fun imageId(url: String): String {
        return hashString("SHA-1", url)
    }

    override fun registerImage(url: String, postfix: String): File {
        val file = tmpDir.resolve(imageId(url) + postfix)
        val entry = ImageEntry(file, id1, nextId2)
        nextId2 += 1
        cache[url] = entry

        return file
    }

    override fun fetchImage(url: String): ImageEntry {
        val partsUrl = if ("\n" in url) { url.split("\n")[0] + "...#${url.length}" } else { url }

        if (cache.containsKey(url)) {
            val fileEntry = cache[url]!!
            LOG.info("url [{}] exits in memory on [{}]", partsUrl, fileEntry.file.toPath())
            return fileEntry
        }

        var file = File(baseDir).absoluteFile.resolve(url)
        if (file.exists()) {
            LOG.info("local file found for url [{}]", url)
        } else {
            file = tmpDir.resolve(imageId(url))
            if (file.exists()) {
                LOG.info("url [{}] exits in disk on [{}]", partsUrl, file)
            } else {
                // try file name with default extension
                val newFile = tmpDir.resolve(imageId(url) + DEFAULT_EXT)
                if (newFile.exists()) {
                    file = newFile
                } else {
                    download(url, file)
                }
            }
        }

        val entry = ImageEntry(file, id1, nextId2)
        nextId2 += 1
        cache[url] = entry
        return entry
    }

    override fun createDelegation(baseDir: String): ImageCache {
        class Delegation: ImageCache by this {
            override val baseDir: String
                get() = baseDir
        }

        return Delegation()
    }

    private fun download(url: String, targetFile: File) {
        LOG.info("downloading [{}] to [{}]", url, targetFile.toPath())
        IOUtils.copy(URL(url).openStream(), targetFile.outputStream())
        LOG.info("downloaded [{}] to [{}]", url, targetFile.toPath())
    }

    override fun close() {
        if (deleteOnExit) {
            tmpDir.deleteRecursively()
        }
    }
}